package com.mobbanana.gamehook.utils

import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.util.Log
import de.robv.android.xposed.XposedBridge
import java.lang.reflect.Field
import java.lang.reflect.Method


/**
 * @Author: imchen
 * @Create: 2020/10/28 17:32
 */
object ReflectUtil {


    fun getField(clazz: Class<*>, name: String): Field? {
        var field: Field = clazz.getDeclaredField(name)
        field.isAccessible = true
        return field;
    }

    fun getHiddenField(clazz: Class<*>, name: String): Field?{
        var metaGetDeclaredMethod =Class::class.java.getDeclaredMethod("getDeclaredMethod")
        var hiddenField:Field= metaGetDeclaredMethod.invoke(clazz, name) as Field
        return hiddenField
    }

    private val TAG: String = "BootstrapClass"

    private var sVmRuntime: Any? = null
    private var setHiddenApiExemptions: Method? = null

    init {
        if (SDK_INT >= Build.VERSION_CODES.P) {
            try {

                val forName = Class::class.java.getDeclaredMethod("forName", String::class.java)
                val getDeclaredMethod = Class::class.java.getDeclaredMethod("getDeclaredMethod", String::class.java, arrayOf(Class::class.java)::class.java) as Method
                val vmRuntimeClass = forName.invoke(null, "dalvik.system.VMRuntime") as Class<*>
                val getRuntime = getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null) as Method
                setHiddenApiExemptions = getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", arrayOf<Class<*>>(Array<String>::class.java)) as Method
                sVmRuntime = getRuntime.invoke(null)
                XposedBridge.log("Init reflect setHiddenApiExemptions")
            } catch (e: Throwable) {
                Log.w(TAG, "reflect bootstrap failed:", e)
            }
        }
    }

    /**
     * make the method exempted from hidden API check.
     *
     * @param method the method signature prefix.
     * @return true if success.
     */
    fun exempt(method: String): Boolean {
        return exempt(*arrayOf(method))
    }

    /**
     * make specific methods exempted from hidden API check.
     *
     * @param methods the method signature prefix, such as "Ldalvik/system", "Landroid" or even "L"
     * @return true if success
     */
    fun exempt(vararg methods: String?): Boolean {
        if (sVmRuntime == null || setHiddenApiExemptions == null) {
            XposedBridge.log("Error: $sVmRuntime or $setHiddenApiExemptions")
            return false
        }
        try {
            setHiddenApiExemptions!!.invoke(sVmRuntime, methods)
            return true
        } catch (e: Throwable) {
            XposedBridge.log(e)
            return false
        }
    }

    /**
     * Make all hidden API exempted.
     *
     * @return true if success.
     */
    fun exemptAll(): Boolean {
        return exempt(*arrayOf("L"))
    }

//    fun aa(){
//        if (SDK_INT >= Build.VERSION_CODES.P) {
//            try {
//                val forName = Class::class.java.getDeclaredMethod("forName", String::class.java)
//                val getDeclaredMethod = Class::class.java.getDeclaredMethod("getDeclaredMethod", String::class.java, arrayOf(Class::class.java)::class.java) as Method
//                val vmRuntimeClass = forName.invoke(null, "dalvik.system.VMRuntime") as Class<*>
//                val getRuntime = getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null) as Method
//                setHiddenApiExemptions = getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", arrayOf<Class<*>>(Array<String>::class.java)) as Method
//                sVmRuntime = getRuntime.invoke(null)
//            } catch (e: Throwable) {
//                Log.w(TAG, "reflect bootstrap failed:", e)
//            }
//        }
//    }

}